﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Asol.Reporting.Support.Data
{
    #region Index obecný (Unique, Multi)
    /// <summary>
    /// Index obecný, unikátní (k jednomu klíči lze evidovat pouze jeden záznam s daty),
    /// postavený nad obecným soupisem dat, z něhož je funkcí vygenerován klíč.
    /// </summary>
    /// <typeparam name="K"></typeparam>
    /// <typeparam name="R"></typeparam>
    public class IndexUnique<K, R> : Dictionary<K, R>
    {
        /// <summary>
        /// Tvorba indexu z dodaného soupisu dat
        /// </summary>
        /// <param name="indexKeyGenerator">Funkce, která z dodané položky dat vytvoří klíč</param>
        public IndexUnique(Func<R, K> indexKeyGenerator)
        {
            this._IndexKeyGenerator = indexKeyGenerator;
        }
        /// <summary>
        /// Tvorba indexu z dodaného soupisu dat
        /// </summary>
        /// <param name="indexKeyGenerator">Funkce, která z dodané položky dat vytvoří klíč</param>
        /// <param name="indexKeyIgnored">Funkce, která pro daný klíč určí, zda se do indexu nemá vkládat (tak lze ignorovat nul nebo void klíče)</param>
        public IndexUnique(Func<R, K> indexKeyGenerator, Func<K, bool> indexKeyIgnored)
        {
            this._IndexKeyGenerator = indexKeyGenerator;
            this._IndexKeyIgnored = indexKeyIgnored;
        }
        /// <summary>
        /// Tvorba indexu z dodaného soupisu dat
        /// </summary>
        /// <param name="indexKeyGenerator">Funkce, která z dodané položky dat vytvoří klíč</param>
        /// <param name="data">Soupis dat, z nichž se index vytvoří</param>
        public IndexUnique(Func<R, K> indexKeyGenerator, IEnumerable<R> data)
        {
            this._IndexKeyGenerator = indexKeyGenerator;
            this.AddRange(data);
        }
        /// <summary>
        /// Tvorba indexu z dodaného soupisu dat
        /// </summary>
        /// <param name="indexKeyGenerator">Funkce, která z dodané položky dat vytvoří klíč</param>
        /// <param name="indexKeyIgnored">Funkce, která pro daný klíč určí, zda se do indexu nemá vkládat (tak lze ignorovat nul nebo void klíče)</param>
        /// <param name="data">Soupis dat, z nichž se index vytvoří</param>
        public IndexUnique(Func<R, K> indexKeyGenerator, Func<K, bool> indexKeyIgnored, IEnumerable<R> data)
        {
            this._IndexKeyGenerator = indexKeyGenerator;
            this._IndexKeyIgnored = indexKeyIgnored;
            this.AddRange(data);
        }
        /// <summary>
        /// Přidá do sebe řadu dalších dat
        /// </summary>
        /// <param name="data"></param>
        public void AddRange(IEnumerable<R> data)
        {
            if (data != null)
            {
                foreach (R item in data)
                    this.Add(item);
            }
        }
        /// <summary>
        /// Přidá do sebe další prvek s daty
        /// </summary>
        /// <param name="item"></param>
        public void Add(R item)
        {
            K key = this._IndexKeyGenerator(item);
            bool ignoreKey = (this._IndexKeyIgnored == null ? false : this._IndexKeyIgnored(key));
            if (!ignoreKey)
            {
                if (this.ContainsKey(key))
                    throw new ArgumentException("IndexUnique: duplicite key " + key.ToString() + " from item " + item.ToString() + "; index now contains " + this.Count.ToString() + " items.");

                this.Add(key, item);
            }
        }
        /// <summary>
        /// Znovu vytvoří this podle aktuálního stavu dat v dodané kolekci
        /// </summary>
        /// <param name="data"></param>
        public void ReIndex(IEnumerable<R> data)
        {
            this.Clear();
            this.AddRange(data);
        }
        /// <summary>
        /// Funkce vytáhne z datové věty (R=záznam) její klíč do indexu (K=Key)
        /// </summary>
        private Func<R, K> _IndexKeyGenerator;
        /// <summary>
        /// Funkce určí, zda daný klíč (vytáhnutý z věty) se má ignorovat = nezakládat do indexu
        /// </summary>
        private Func<K, bool> _IndexKeyIgnored;
    }
    /// <summary>
    /// Index obecný, multipoložkový (k jednomu klíči lze evidovat řadu záznamů, které generují tento klíč),
    /// postavený nad obecným soupisem dat, z něhož je funkcí vygenerován klíč.
    /// </summary>
    /// <typeparam name="K"></typeparam>
    /// <typeparam name="R"></typeparam>
    public class IndexMulti<K, R> : Dictionary<K, List<R>>
    {
        /// <summary>
        /// Tvorba indexu z dodaného soupisu dat
        /// </summary>
        /// <param name="indexKeyGenerator">Funkce, která z dodané položky dat vytvoří klíč</param>
        public IndexMulti(Func<R, K> indexKeyGenerator)
        {
            this._IndexKeyGenerator = indexKeyGenerator;
        }
        /// <summary>
        /// Tvorba indexu z dodaného soupisu dat
        /// </summary>
        /// <param name="indexKeyGenerator">Funkce, která z dodané položky dat vytvoří klíč</param>
        /// <param name="indexKeyIgnored">Funkce, která pro daný klíč určí, zda se do indexu nemá vkládat (tak lze ignorovat nul nebo void klíče)</param>
        public IndexMulti(Func<R, K> indexKeyGenerator, Func<K, bool> indexKeyIgnored)
        {
            this._IndexKeyGenerator = indexKeyGenerator;
            this._IndexKeyIgnored = indexKeyIgnored;
        }
        /// <summary>
        /// Tvorba indexu z dodaného soupisu dat
        /// </summary>
        /// <param name="indexKeyGenerator">Funkce, která z dodané položky dat vytvoří klíč</param>
        /// <param name="data">Soupis dat, z nichž se index vytvoří</param>
        public IndexMulti(Func<R, K> indexKeyGenerator, IEnumerable<R> data)
        {
            this._IndexKeyGenerator = indexKeyGenerator;
            this.AddRange(data);
        }
        /// <summary>
        /// Tvorba indexu z dodaného soupisu dat
        /// </summary>
        /// <param name="indexKeyGenerator">Funkce, která z dodané položky dat vytvoří klíč</param>
        /// <param name="indexKeyIgnored">Funkce, která pro daný klíč určí, zda se do indexu nemá vkládat (tak lze ignorovat nul nebo void klíče)</param>
        /// <param name="data">Soupis dat, z nichž se index vytvoří</param>
        public IndexMulti(Func<R, K> indexKeyGenerator, Func<K, bool> indexKeyIgnored, IEnumerable<R> data)
        {
            this._IndexKeyGenerator = indexKeyGenerator;
            this._IndexKeyIgnored = indexKeyIgnored;
            this.AddRange(data);
        }
        /// <summary>
        /// Přidá do sebe řadu dalších dat
        /// </summary>
        /// <param name="data"></param>
        public void AddRange(IEnumerable<R> data)
        {
            if (data != null)
            {
                foreach (R item in data)
                    this.Add(item);
            }
        }
        /// <summary>
        /// Přidá do sebe jednu další položku.
        /// Pokud klíč z ní vygenerovaný v indexu nemáme, založí se pro klíč další položka do indexu, položka = List obsahující jednotlivé záznamy se shodným klíčem.
        /// Pokud klíč z položky vygenerovaný již v indexu bude, přidá se položka do jeho soupisu.
        /// </summary>
        /// <param name="item"></param>
        public void Add(R item)
        {
            K key = this._IndexKeyGenerator(item);
            List<R> list;
            if (!this.TryGetValue(key, out list))
            {
                list = new List<R>();
                this.Add(key, list);
            }
            list.Add(item);
        }
        /// <summary>
        /// Znovu vytvoří this podle aktuálního stavu dat v dodané kolekci
        /// </summary>
        /// <param name="data"></param>
        public void ReIndex(IEnumerable<R> data)
        {
            this.Clear();
            this.AddRange(data);
        }
        /// <summary>
        /// Funkce vytáhne z datové věty (R=záznam) její klíč do indexu (K=Key)
        /// </summary>
        private Func<R, K> _IndexKeyGenerator;
        /// <summary>
        /// Funkce určí, zda daný klíč (vytáhnutý z věty) se má ignorovat = nezakládat do indexu
        /// </summary>
        private Func<K, bool> _IndexKeyIgnored;
    }
    #endregion
}
